/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.service.datasource.mybatis.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import yhao.infra.common.ContextHolder;
import yhao.infra.service.datasource.usemaster.UseMasterContextKey;

import java.sql.Connection;
import java.util.Properties;

/**
 * 基础拦截器
 * <ul>
 *     <li>处理对主从库的查询</li>
 * </ul>
 *
 * @author yoara
 */
@Intercepts(@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class}))
public class BaseInterceptor extends AbstractInterceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        RoutingStatementHandler statement = (RoutingStatementHandler) invocation.getTarget();
        BoundSql boundSql = statement.getBoundSql();
        String sql = boundSql.getSql();
        Connection con = (Connection) invocation.getArgs()[0];
        String preSql = sql.trim().substring(0, 6).toLowerCase();
        if ("select".equals(preSql)) {
            con.setReadOnly(true);
        }
        if(ContextHolder.getContextData(UseMasterContextKey.class)!=null){
            con.setReadOnly(false);
        }
        Object result;
        try {
            result = invocation.proceed();
        } finally {
            con.setReadOnly(false);
        }
        String sqlStr = null;
        if(boundSql.getParameterObject()!=null){
            //使用jackson做映射的原因是返回给前端的数据是通过fastjson包装的，一些敏感字段会被hidden。
            //合理的使用jackson和fastjson搭配使用，可以实现类似于下面这个参考的效果。
            ObjectMapper mapper=new ObjectMapper();
            sqlStr = mapper.writeValueAsString(boundSql.getParameterObject());
            //sqlStr = JSONObject.toJSONString(boundSql.getParameterObject());
        }
        SqlStatInterceptor.initStat(new StringBuffer(ContextHolder.getDataSource()).append(":")
                .append(sql).append(":").append(sqlStr).toString());
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
